import { booleanAttribute, Directive, ElementRef, input, OnInit, Renderer2, inject } from '@angular/core';
import { MatTooltip, TooltipPosition } from '@angular/material/tooltip';
import { ContentObserver } from '@angular/cdk/observers';
import { merge } from 'rxjs';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';

/**
 * 文本截断并显示 Tooltip 指令
 * 
 * 功能：
 * - 自动截断超出容器宽度的文本，显示省略号（...）
 * - 仅在文本溢出时显示 Tooltip
 * - 响应式检测内容变化
 * - 支持自定义 Tooltip 位置和启用状态
 * 
 * 使用方式：
 * ```html
 * <!-- 自动检测元素内容 -->
 * <h2 cneTruncateWithTooltip>{{ longText }}</h2>
 * 
 * <!-- 自定义 Tooltip 内容 -->
 * <h2 cneTruncateWithTooltip="Custom tooltip text">Content</h2>
 * 
 * <!-- 自定义位置 -->
 * <h2 cneTruncateWithTooltip position="below">Content</h2>
 * 
 * <!-- 禁用 Tooltip -->
 * <h2 cneTruncateWithTooltip [tooltipEnabled]="false">Content</h2>
 * ```
 */
@Directive({
  selector: '[cneTruncateWithTooltip]',
  standalone: true,
  hostDirectives: [{
    directive: MatTooltip,
    inputs: ['matTooltipClass', 'matTooltipTouchGestures'],
  }]
})
export class TruncateWithTooltipDirective implements OnInit {

  /** 可选的 Tooltip 文本内容 */
  text = input<string>(undefined, {alias: 'cneTruncateWithTooltip'});

  /** 是否启用 Tooltip */
  tooltipEnabled = input(true, {transform: booleanAttribute});

  /** Tooltip 位置 */
  position = input<TooltipPosition>('above');

  private elementRef = inject(ElementRef<HTMLElement>); // 获取宿主元素引用
  private renderer = inject(Renderer2); // 用于安全地操作 DOM 样式
  private tooltip = inject(MatTooltip); // Tooltip 指令实例
  private contentObserver = inject(ContentObserver); // 监听 DOM 内容变化

  constructor() {
    // 监听文本输入和 DOM 内容变化
    merge(
      toObservable(this.text), // 响应 text input 变化
      this.contentObserver.observe(this.elementRef) // 响应 DOM 内容变化
    ).pipe(
      takeUntilDestroyed() // 组件销毁时自动取消订阅
    ).subscribe(() => {
      // 使用输入文本或元素内容作为 tooltip 消息
      this.tooltip.message = this.text() || this.elementRef.nativeElement.innerText;
    });
  }

  ngOnInit(): void {
    this.applyTruncationStyles();
    this.tooltip.position = this.position();
    this.showTooltipOnOverflow();
  }

  /**
   * 重写 Tooltip 的显示逻辑，仅在文本溢出时显示
   */
  private showTooltipOnOverflow(): void {
    // 保存原始 show 方法并绑定 this 上下文
    const originalShow = this.tooltip.show.bind(this.tooltip);
    
    // 重写 tooltip.show 方法，实现条件显示逻辑
    this.tooltip.show = function(this: TruncateWithTooltipDirective) {
      const ctx = this; // this 指向当前指令实例
      if (ctx.tooltipEnabled() && ctx.isOverflown()) {
        originalShow.apply(ctx.tooltip); // 调用原始的 show 方法
      }
    }.bind(this);
  }

  /**
   * 应用文本截断样式
   */
  private applyTruncationStyles(): void {
    // 强制单行显示，不换行
    this.renderer.setStyle(this.elementRef.nativeElement, 'white-space', 'nowrap');
    // 隐藏溢出内容
    this.renderer.setStyle(this.elementRef.nativeElement, 'overflow', 'hidden');
    // 溢出时显示省略号
    this.renderer.setStyle(this.elementRef.nativeElement, 'text-overflow', 'ellipsis');
  }

  /**
   * 检测元素是否溢出
   * 
   * @returns true 如果元素的 scrollWidth 大于 clientWidth
   */
  private isOverflown(): boolean {
    // 比较可视宽度和内容宽度，判断是否溢出
    return this.elementRef.nativeElement.clientWidth < this.elementRef.nativeElement.scrollWidth;
  }
}
